package linguo;

import gui.GameGUI;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.File;

import linguo.input.KeyInput;
import linguo.input.KeyboardHandler;
import linguo.level.Board;
import linguo.level.mapObjects.Coleccionable;
import linguo.player.BulletHandler;
import linguo.player.Robot;
import linguo.player.RobotCivil;
import linguo.player.RobotMilitar;
import linguo.sprites.BufferedImageLoader;

/**
 * Clase principal encargada del manejo central del juego
 * 
 * @author Corpaz German
 * @author Frank Andres
 */
public class Game extends Canvas implements Runnable {
	private static final long serialVersionUID = 1L;

	private final int WIDTH = 800;
	private final int HEIGHT = 600;
	private GameGUI gameGUI;

	private Thread thread;
	private BufferStrategy strategy;
	private boolean running = false;
	private GameState state = GameState.WAITING;

	private BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
	private BufferedImage grid;
	private boolean showGrid = false, showFPS = true;

	private Board board;
	private Robot robot;
	private BulletHandler bulletHandler;
	protected File nivelActual;

	public Game(GameGUI gameGUI) {
		this.setIgnoreRepaint(true);
		this.setPreferredSize(new Dimension(WIDTH, HEIGHT));
		this.setVisible(true);
		this.gameGUI = gameGUI;

		addKeyListener(new KeyInput(new KeyboardHandler(this)));
	}

	
	/**
	 * Inicia la ejecucion del thread
	 */
	public synchronized void start() {
		running = true;
		thread = new Thread(this, "Display");
		thread.start();
	}

	/**
	 * Termina el juego
	 */
	public synchronized void stop() {
		running = false;
		thread.interrupt();
	}

	/**
	 *  Comienza el ciclo de juego
	 */
	public void run() {
		init();

		this.requestFocus();
		long lastTime = System.nanoTime();
		long timer = System.currentTimeMillis();
		final double ns = 1000000000.0 / 60.0;
		double delta = 0;
		int frames = 0;
		int updates = 0;

		while (running) {
			long now = System.nanoTime();
			delta += (now - lastTime) / ns;
			lastTime = now;
			while (delta >= 1) {
				update();
				updates++;
				delta--;
			}
			render();
			frames++;

			if (System.currentTimeMillis() - timer > 1000) {
				timer += 1000;
				System.out.println(updates + " ups, " + frames + " fps"); // para testeo
				updates = 0;
				frames = 0;
			}

		}
		stop();
	}

	/**
	 * Inicializa las entidades, personajes, imagenes, etc.
	 */
	public void init() {
		BufferedImageLoader loader = new BufferedImageLoader();
		String gridpath="/resources/grid"+Data.SPRITE_SIZE+".png";
		grid = loader.loadImage(gridpath); 

		if (gameGUI.getRobotType() == 1) robot = new RobotMilitar(this, board, 0, 0);
		else robot = new RobotCivil(this, board, 0, 0);
		
		board = new Board(this, gameGUI.getNivel(),nivelActual);
		
		robot.setBoard(board);
		bulletHandler = new BulletHandler();

		state = GameState.INGAME;
	}

	/**
	 * Actualiza los graficos del juego
	 */
	public void update() {
		if (state == GameState.INGAME) {
			bulletHandler.update();
			board.update();
		}
	}

	// Variables FPS Counter 
	private long nextSecond = System.currentTimeMillis() + 1000;
	private int framesInLastSecond = 0;
	private int framesInCurrentSecond = 0;

	/**
	 * Realiza todos los render para mostrar los graficos en pantalla
	 */
	public void render() {
		strategy = getBufferStrategy();
		if (strategy == null) {
			createBufferStrategy(3);
			return;
		}

		Graphics g = strategy.getDrawGraphics();
		/////////////////////////////////////////////////////////
		g.drawImage(image, 0, 0, getWidth(), getHeight(), this);

		board.render(g);

		if (showGrid) g.drawImage(grid, 0, 0, null);

		bulletHandler.render(g);
		robot.render(g);

		renderStats(g);

		// FPS Counter
		long currentTime = System.currentTimeMillis();
		if (currentTime > nextSecond) {
			nextSecond += 1000;
			framesInLastSecond = framesInCurrentSecond;
			framesInCurrentSecond = 0;
		}
		framesInCurrentSecond++;
		if (showFPS) g.drawString(framesInLastSecond + " fps", WIDTH - 52, HEIGHT - 10);
		/////////////////////////////////////////////////////////
		g.dispose();
		strategy.show();
	}

	/**
	 * Activa o desactiva la grilla a mostrar en pantalla.
	 */
	public void toogleGrid() {
		this.showGrid = !showGrid;
	}

	/**
	 * Activa o desactiva los fps a mostrar en pantalla.
	 */
	public void toogleFPS() {
		this.showFPS = !showFPS;
	}

	/**
	 * Muestra la informacion del robot.
	 * Nafta disponible, puntos obtenidos, etc.
	 * 
	 * @param g
	 */
	private void renderStats(Graphics g) {
		int posX = 50, posY = HEIGHT - 42;

		g.setColor(Color.BLACK);
		g.fillRect(0, posY - 14, WIDTH - 1, 60);

		g.setColor(Color.WHITE);
		g.drawString("Puntaje: " + robot.getScore(), posX + 15, posY + 20);

		g.drawString("Nafta: " + robot.getNafta() + " /" + robot.getMaxNafta(), posX + 150, posY + 9);
		g.drawString("Municion: " + robot.getMunicion() + " /" + robot.getMaxMunicion(), posX + 150, posY + 32);

		g.drawString("Objetos En Baul: " + robot.getCantColeccionable(), posX + 310, posY + 9);
		g.drawString("Ultimo: ", posX + 372, posY + 32);
		Coleccionable ultimo = robot.getLastColeccionable();
		if (ultimo != null) g.drawImage(ultimo.getImage(), posX + 420, posY + 9, null);

		g.drawString("Pincel: " + robot.getEstadoPincel(), posX + 540, posY + 9);
		String color = robot.getColorPincel() == 1 ? "negro" : "blanco";
		g.drawString("Color: " + color, posX + 540, posY + 32);
	}

	/**
	 * Devuelve el manejador de balas
	 * @return Manejador de balas
	 */
	public BulletHandler getBulletHandler() {
		return bulletHandler;
	}

	/**
	 * Obtiene el ancho del canvas
	 * 
	 * @return el ancho del canvas
	 */
	public int getWIDTH() {
		return WIDTH;
	}

	/**
	 * Obtiene el alto del canvas
	 * 
	 * @return el alto del canvas
	 * 
	 */
	public int getHEIGHT() {
		return HEIGHT;
	}

	/**
	 * Obtiene el robot jugador
	 * 
	 * @return el robot jugador
	 */
	public Robot getRobot() {
		return robot;
	}

	/**
	 * Avisa a la interfaz grafica que el juego va a terminar
	 */
	public void terminarJuego() {
		gameGUI.terminarJuego();
	}
	
	/**
	 * Finaliza el juego indicando que el jugador gano
	 */
	public void ganarJuego() {
		robot.incrementarScore(100 + robot.getCantColeccionable() * 19);
		state = GameState.GAMEOVER;
		gameGUI.ganarJuego(robot.getScore());
	}

	/**
	 * Finaliza el juego indicando que el jugador perdio
	 */
	public void perderJuego() {
		state = GameState.GAMEOVER;
		gameGUI.perderJuego(robot.getScore());
	}

	/**
	 * Reinicia el juego volviendo todo el contenido del nivel a su estado incial
	 */
	public void reiniciarJuego() {
		init();
		this.requestFocus();
	}

	/**
	 * Guarda el archivo que se selecciono en la pantalla principal para mandarselo a board
	 * @param Archivo seleccionado
	 */
	public void setNivel(File f) {
		nivelActual = f;
	}

	/**
	 * Devuelve el estado del juego
	 * @return estado
	 */
	public GameState getState() {
		return state;
	}

}
